#include "include/MessageParserTool.h"

#include "core/strings/string_convert.h"
#include "core/strings/stringconv_posix.h"
#include "core/sync/lock.h"

#ifndef WIN32
#include <pthread.h>
#endif

namespace qb {

int safe_copy(char* szDestination, size_t isize, const char* szSource) {
  if (!szDestination || isize == 0) return 0;
  if (strlen(szSource) > isize) {
    std::string strinfo = std::string("Field:") + szSource + "overflow!!";
    // OutputDebugString(strinfo.c_str());
  }
  memset(szDestination, 0, isize);
  memcpy(szDestination, szSource, isize);
  return strlen(szDestination);
}

///////////////////////////////////////////////////////////////////////////////////
////class ConvertHub
class ConvertHub {
 public:
  ConvertHub();
  ~ConvertHub();
  qb::base::CConvertFast* GetConverter();

 protected:
  qb::base::CConvertFast* NewConverter(DWORD tid);

 private:
  enum { CONVERT_COUNT = 7 };  // 小的质数,最多允许
  struct ConverterInfo {
    qb::base::CConvertFast* converter;  // 字符转码器
    DWORD threadid;                     // 该线程的线程id
  };
  ConverterInfo m_converters[CONVERT_COUNT];  // 字符转码器的数组,可能为空
  int m_last_index;          // 上一次的索引,避免搜索数组
  volatile bool m_deleting;  // 用于告诉用户当前正在析构
  qb::base::CSLock m_lock;   // 用于获取字符转码器的锁
};

/////////////////////////////////////////////////////////////////////////////
////class ConvertHub
ConvertHub::ConvertHub() : m_last_index(0), m_deleting(false) {
  for (int i = 0; i < CONVERT_COUNT; i++) {
    m_converters[i].converter = NULL;
    m_converters[i].threadid = 0;
  }
}

ConvertHub::~ConvertHub() {
  qb::base::ScopedLock<qb::base::CSLock> scoped(m_lock);
  m_deleting = true;
  for (int i = 0; i < CONVERT_COUNT; i++) {
    if (m_converters[i].converter) {
      delete m_converters[i].converter;
      m_converters[i].converter = NULL;
    }
  }
}

base::CConvertFast* ConvertHub::GetConverter() {
  // 快速拿到当前线程的字符编码转码器
  // 由于CONVERT_COUNT过于小,直接搜索,并保存上一次的位置m_last_index
#ifdef WIN32
  DWORD tid = GetCurrentThreadId();
#else
  DWORD tid = (DWORD)pthread_self();
#endif

  qb::base::ScopedLock<qb::base::CSLock> scoped(m_lock);
  for (int i = m_last_index; i < m_last_index + CONVERT_COUNT; i++) {
    int index = i % CONVERT_COUNT;
    if (m_converters[index].converter != NULL &&
        m_converters[index].threadid == tid) {
      m_last_index = index;
      return m_converters[index].converter;
    }
  }
  return NewConverter(tid);
}

base::CConvertFast* ConvertHub::NewConverter(DWORD tid) {
  // 首次使用分配当前线程(tid)的CConvertFast
  // 一次性开销
  if (m_deleting) return NULL;
  int start = tid % CONVERT_COUNT;
  for (int i = start; i < start + CONVERT_COUNT; i++) {
    int index = i % CONVERT_COUNT;
    if (m_converters[index].converter == NULL) {
      m_last_index = index;
      m_converters[index].converter = new qb::base::CConvertFast();
      m_converters[index].threadid = tid;
      return m_converters[index].converter;
    }
  }

  return NULL;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
////class CParserTool
static ConvertHub g_converter_hub;
const char* CParserTool::GetString(const qb::proto::VariantRecord* msg,
                                   const char* name) {
  // 改进性能
  // 1.参数 name 不必构造成std::string
  // 2.utf8 转化为gbk,不必反复释放内存,始终使用对象m_converter里面的内存
  // 3.返回不必构造std::string
  // 要求:访问都来自同一个线程,m_converter不是线程安全的
  // 即使在这里增加lock,也不安全
  if (name) {
    const char* str = 0;
    size_t len = 0;
    if (msg->refString(name, str, len)) {
      return ToGBK(str, len);
    }
  }

  static std::string blank = "";
  return blank.c_str();
}

const char* CParserTool::GetString(const ProtoMessage& msg, const char* name) {
  // 改进性能
  // 1.参数 name 不必构造成std::string
  // 2.utf8 转化为gbk,不必反复释放内存,始终使用对象m_converter里面的内存
  // 3.返回不必构造std::string
  // 要求:访问都来自同一个线程,m_converter不是线程安全的
  // 即使在这里增加lock,也不安全
  if (name) {
    const char* str = 0;
    size_t len = 0;
    if (msg.RefString(name, str, len)) {
      return ToGBK(str, len);
    }
  }

  static std::string blank = "";
  return blank.c_str();
}

const char* CParserTool::GetString(const ProtoMap& msgmap, const char* name) {
  // 改进性能
  // 1.参数 name 不必构造成std::string
  // 2.utf8 转化为gbk,不必反复释放内存,始终使用对象m_converter里面的内存
  // 3.返回不必构造std::string
  // 要求:访问都来自同一个线程,m_converter不是线程安全的
  // m_converter_hub 用来处理多个线程调用此
  if (name) {
    const char* str = 0;
    size_t len = 0;
    if (msgmap.RefString(name, str, len)) {
      return ToGBK(str, len);
    }
  }

  static std::string blank = "";
  return blank.c_str();
}

const char* CParserTool::ToGBK(const char* utf8, int len) {
  if (utf8 != NULL && len > 0) {
    qb::base::CConvertFast* converter = g_converter_hub.GetConverter();
    if (converter != NULL) {
      const char* gbkstr = converter->ToGbkEx(utf8, len);
      if (CParserTool::IsCheckEnable()) {
        base::CConvertFast conv;
        const char* ret = conv.ToGbk(utf8, len);  // syy
      }
      return gbkstr;
    } else {
      static std::string gbk;
      gbk = UTF8ToGBK(std::string(utf8, len));
      return gbk.c_str();
    }
  }

  static std::string blank = "";
  return blank.c_str();
}

std::string CParserTool::UTF8ToGBK(const std::string& strUTF8) {
#ifdef WIN32
  int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0);
  unsigned short* wszGBK = new (std::nothrow) unsigned short[len + 1];
  if (wszGBK == NULL) return strUTF8;
  memset(wszGBK, 0, len * 2 + 2);
  MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)strUTF8.c_str(), -1, (LPWSTR)wszGBK,
                      len);

  len = WideCharToMultiByte(/*CP_ACP*/ 936, 0, (LPCWSTR)wszGBK, -1, NULL, 0,
                            NULL, NULL);
  char* szGBK = new (std::nothrow) char[len + 1];
  if (szGBK == NULL) {
    delete[] wszGBK;
    return strUTF8;
  }
  memset(szGBK, 0, len + 1);
  WideCharToMultiByte(/*CP_ACP*/ 936, 0, (LPCWSTR)wszGBK, -1, szGBK, len, NULL,
                      NULL);
  // strUTF8 = szGBK;
  std::string strTemp(szGBK);
  delete[] szGBK;
  delete[] wszGBK;
#else
  USES_CONVERSION;
  char* outptr = UTF8_TO_GBK(strUTF8.c_str());
  std::string strTemp(outptr);
#endif
  return strTemp;
}

bool CParserTool::MessageGetTime(const ProtoMessage& msg,
                                 const std::string& name, time_t& t) {
  int tmptime = 0;
  bool bRet = msg.getInt32(name, tmptime);
  t = static_cast<time_t>(tmptime);
  return bRet;
}

bool CParserTool::MessageGetTime(const qb::proto::VariantRecord* msg,
                                 const std::string& name, time_t& t) {
  int tmptime = 0;
  bool bRet = msg->getInt32(name, tmptime);
  t = static_cast<time_t>(tmptime);
  return bRet;
}

bool CParserTool::MessageGetInt64toTime(const ProtoMessage& msg,
                                        const std::string& name, time_t& t) {
  qb::base::msg::int64_t tmptime = 0;
  bool bRet = msg.getInt64(name, tmptime);
  int tempt = tmptime / 1000;
  t = static_cast<time_t>(tempt);
  return bRet;
}

bool CParserTool::MessageGetDoubleToFloat(const ProtoMessage& msg,
                                          const std::string& name,
                                          float& fvalue) {
  double dval = 0.0;
  if (msg.getDouble(name, dval)) {
    fvalue = static_cast<float>(dval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetUint32ToDWORD(const ProtoMessage& msg,
                                          const std::string& name,
                                          DWORD& dwValue) {
  unsigned int nval = 0;
  if (msg.getUint32(name, nval)) {
    dwValue = static_cast<DWORD>(nval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetUint64ToDWORD(const ProtoMessage& msg,
                                          const std::string& name,
                                          DWORD& dwValue) {
  qb::base::msg::uint64_t nval = 0;
  if (msg.getUint64(name, nval)) {
    dwValue = static_cast<DWORD>(nval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetInt64ToLong(const ProtoMessage& msg,
                                        const std::string& name, long& lValue) {
  qb::base::msg::int64_t nval = 0;
  if (msg.getInt64(name, nval)) {
    lValue = static_cast<long>(nval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetUint64ToDouble(const ProtoMessage& msg,
                                           const std::string& name,
                                           double& dwValue) {
  qb::base::msg::uint64_t dval = 0;
  if (msg.getUint64(name, dval)) {
    dwValue = static_cast<double>(dval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetTime(const ProtoMap& msg, const std::string& name,
                                 time_t& t) {
  int tmptime = 0;
  bool bRet = msg.getInt32(name, tmptime);
  t = static_cast<time_t>(tmptime);
  return bRet;
}

bool CParserTool::MessageGetInt64toTime(const ProtoMap& msg,
                                        const std::string& name, time_t& t) {
  qb::base::msg::int64_t tmptime = 0;
  bool bRet = msg.getInt64(name, tmptime);
  int tempt = tmptime / 1000;
  t = static_cast<time_t>(tempt);
  return bRet;
}

bool CParserTool::MessageGetDoubleToFloat(const ProtoMap& msg,
                                          const std::string& name,
                                          float& fvalue) {
  double dval = 0.0;
  if (msg.GetDouble(name.c_str(), dval)) {
    fvalue = static_cast<float>(dval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetUint32ToDWORD(const ProtoMap& msg,
                                          const std::string& name,
                                          DWORD& dwValue) {
  unsigned int nval = 0;
  if (msg.GetUInt32(name.c_str(), nval)) {
    dwValue = static_cast<DWORD>(nval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetUint64ToDWORD(const ProtoMap& msg,
                                          const std::string& name,
                                          DWORD& dwValue) {
  qb::base::msg::uint64_t nval = 0;
  if (msg.GetUInt64(name.c_str(), nval)) {
    dwValue = static_cast<DWORD>(nval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetInt64ToLong(const ProtoMap& msg,
                                        const std::string& name, long& lValue) {
  qb::base::msg::int64_t nval = 0;
  if (msg.GetInt64(name.c_str(), nval)) {
    lValue = static_cast<long>(nval);
    return true;
  }
  return false;
}

bool CParserTool::MessageGetUint64ToDouble(const ProtoMap& msg,
                                           const std::string& name,
                                           double& dwValue) {
  qb::base::msg::uint64_t dval = 0;
  if (msg.GetUInt64(name.c_str(), dval)) {
    dwValue = static_cast<double>(dval);
    return true;
  }
  return false;
}

int CParserTool::GetFuncIDbyType(const std::string& stype) {
  int iFunID = atoi(stype.c_str());
  return iFunID;
}

std::string CParserTool::GetTypebyFuncID(int nFuncID) {
  std::string stype;
  {
    char str[64] = "";
    sprintf(str, "%d", nFuncID);
    stype = str;
  }
  return stype;
}

void CParserTool::GetDoubleToString(const char* fieldName, std::string& dest,
                                    const ProtoMap& VMap) {
  double dDest = 0.0;
  VMap.GetDouble(fieldName, dDest);
  char buffer[20] = {0};
  sprintf(buffer, "%f", dDest);
  dest = std::string(buffer);
}

bool CParserTool::IsStringEqual(const std::string& str, const char* s) {
  int size = str.size();
  const char* base = str.c_str();
  for (int i = 0; i < size; i++) {
    if (s[i] == 0 || base[i] != s[i]) return false;
  }
  return s[size] == 0 ? true : false;
}

bool CParserTool::VariantRefString(const ProtoValue& vt, std::string& out) {
  const char* str = 0;
  size_t len = 0;
  if (!vt.RefString(str, len)) return false;
  out = (str && len > 0) ? std::string(str, len) : "";
  return true;
}

void CParserTool::MapRefString(const ProtoMap& vmap, const char* name,
                               std::string& out) {
  const char* str = 0;
  size_t len = 0;
  if (name != NULL && vmap.RefString(name, str, len) && str && len > 0)
    out = std::string(str, len);
  else
    out = "";
}

bool CParserTool::IsVerbosLogEnable() {
  static bool init = false;
  static bool enable = false;
  if (!init) {
    init = true;
  }
  return enable;
}

bool CParserTool::IsCheckEnable() {
  static bool init = false;
  static bool enalbe = false;
  if (!init) {
    init = true;
    //	enalbe =
    //qb::base::cfg::GetConfigBool(qb::base::kBaseConfigCheckProtoMessage);
    ////syy
  }
  return enalbe;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// class MsgDumpCfg
class MsgDumpCfg {
 public:
  static MsgDumpCfg& get();
  void load();
  bool passfilter(int len, int func) const;

 private:
  MsgDumpCfg();

 private:
  int m_func_start;
  int m_func_end;
  int m_len_start;
  int m_len_end;
  bool m_enable;
};

MsgDumpCfg::MsgDumpCfg()
    : m_enable(false),
      m_len_end(-1),
      m_len_start(0),
      m_func_start(-1),
      m_func_end(-1) {
  load();
}

void MsgDumpCfg::load() {
  /*	m_enable = BOOLTEST(kProfileDumpMessageEnable);
      string func = AfxGetConfig().GetString(kProfileDumpMessageFilterFuncID);
      string lens = AfxGetConfig().GetString(kProfileDumpMessageFilterLength);
      std::vector<qb::base::strinfo> tokens;
      if (qb::base::StringUtil::SplitStringInfo(func.c_str(), func.size(), "
     \t,:-", tokens) >= 2)
      {
          m_func_start = atoi(func.c_str() + tokens[0].pos);
          m_func_end = atoi(func.c_str() + tokens[1].pos);
      }
      tokens.clear();
      if (qb::base::StringUtil::SplitStringInfo(lens.c_str(), lens.size(), "
     \t,:-", tokens) >= 2)
      {
          m_len_start = atoi(lens.c_str() + tokens[0].pos);
          m_len_end = atoi(lens.c_str() + tokens[1].pos);
      }*/ //syy
}

bool MsgDumpCfg::passfilter(int len, int func) const {
  return m_enable && ((len >= m_len_start && len <= m_len_end) ||
                      (func >= m_func_start && func <= m_func_end));
}

MsgDumpCfg& MsgDumpCfg::get() {
  static MsgDumpCfg mdc;
  return mdc;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// class DecodeMetric
DecodeMetric::DecodeMetric(double* ptime, const char* buf, int len, int funcid)
    : m_buffer(buf), m_length(len), m_funcid(funcid) {}

DecodeMetric::~DecodeMetric() {
  //	*m_ptime = (double)m_timer.elapsed();
  if (CParserTool::IsCheckEnable()) {
    unsigned int usec = 0;  // (unsigned int)(*m_ptime);
    if (MsgDumpCfg::get().passfilter(m_length, m_funcid)) {
      /*	char fname[MAX_PATH] = { 0 };
          SYSTEMTIME st;
          GetLocalTime(&st);
          sprintf_s(fname,
         ".\\data\\message\\%08d_%08d_%08d_%02d%02d%02d_%2d%02d%2d_%3d.bin",
              m_funcid, m_length, usec,
              st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
         st.wMilliseconds
          );
          qb::base::StringUtil::SaveFileContent(m_buffer, m_length, fname);*/
    }
  }
}

}  // namespace qb
